package com.lsh.oms.core.service.order;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lsh.base.common.model.CommonResult;
import com.lsh.oms.api.model.order.receipt.BatchReceiptHeadDto;
import com.lsh.oms.api.model.order.receipt.ReceiptHeadDto;
import com.lsh.oms.api.model.order.sign.SignHeadDto;
import com.lsh.oms.core.constant.Constants;
import com.lsh.oms.core.constant.ReturnCode;
import com.lsh.oms.core.enums.*;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.order.*;
import com.lsh.oms.core.model.payment.BillPaymentInfo;
import com.lsh.oms.core.proxy.service.DmallProxy;
import com.lsh.oms.core.redis.RedisTemplate;
import com.lsh.oms.core.service.asynservice.OmsEventBus;
import com.lsh.oms.core.service.other.ShippingOrderService;
import com.lsh.oms.core.service.other.SignOrderService;
import com.lsh.oms.core.service.payment.BillPaymentService;
import com.lsh.oms.core.service.sign.core.SignService;
import com.lsh.oms.core.service.sms.SmsService;
import com.lsh.oms.core.utils.MD5Util;
import com.lsh.oms.core.utils.StringTool;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 订单发货service
 *
 * @author peter
 */
@Service
public class OrderReceiptService extends AbstractOrderService {

    private static Logger logger = LoggerFactory.getLogger(OrderReceiptService.class);

    @Autowired
    private OrderQueryService orderQueryService;

    @Autowired
    private ShippingOrderService shippingService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private BillPaymentService billPaymentService;

    @Resource
    private SignService signs;

    @Resource
    private SignOrderService signOrders;

    @Autowired
    private DmallProxy dmallProxy;

    @Autowired
    private SmsService smsService;


    /**
     * 创建签收单
     *
     * @param receiptHeadDto 请求参数
     * @param logSign        唯一标识
     * @return 返回信息
     */
    //@Transactional
    public CommonResult<Object> createReceiptOrder(ReceiptHeadDto receiptHeadDto, String logSign) {
        CommonResult<Object> res = new CommonResult<>();
        OrderSignHead orderSignHead = this.genSignOrder(receiptHeadDto, logSign);
        if (orderSignHead != null) {
            res.setCode(ReturnCode.REQUEST_SUCCESS);
            res.setData(orderSignHead);

            if (PayStatus.PAID.getValue().equals(orderSignHead.getPayStatus())) {
//                已支付状态，回调商城退货退款
                OmsEventBus.post(orderSignHead);
            }

        } else {
            res.setCode(ReturnCode.REQUEST_FAIL);
        }

        return res;
    }


    /**
     * 创建签收单
     *
     * @param receiptHeadDto 请求参数
     * @param logSign        唯一标识
     * @return 返回信息
     */
    public CommonResult<Object> calProxy(ReceiptHeadDto receiptHeadDto, String logSign) {
        CommonResult<Object> res = new CommonResult<>();


        Long shippingOrderId = receiptHeadDto.getShippingOrderId();

        OrderShippingHeadWithBLOBs osh = this.shippingService.selectOneByShipCode(shippingOrderId, true);
        if (osh == null) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "此订单未生成发货单.请查询");
        }

        OrderHead orderHead = orderQueryService.findByCode(osh.getOrderId(), true, false);
        if (orderHead == null) {
            throw new BusinessException(ReturnCode.FIND_ORDER_IS_NULL, "用户订单号未查询到订单");
        }
        //签收发生在发货以后
        if (orderHead.getOrderStatus().compareTo(OrderStatus.ORDER_SORTING.getIndex()) < 0) {
            throw new BusinessException(ReturnCode.ORDER_STATUS_ERROR, "订单状态是" + OrderStatus.getName(orderHead.getOrderStatus()) + "，请检验。");
        }
        CalOrderContent calOrderContent = this.proxyCal(receiptHeadDto, osh, logSign);
        if (calOrderContent != null) {
            calOrderContent.fixMoney();
            res.setCode(ReturnCode.REQUEST_SUCCESS);
            res.setData(calOrderContent);

        } else {
            res.setCode(ReturnCode.REQUEST_FAIL);
        }

        return res;
    }


    /**
     * 创建签收单
     *
     * @param batchReceiptHeadDto 请求参数
     * @param logSign             唯一标识
     * @return 返回信息
     */
    @Transactional
    public CommonResult<Object> batchCreateReceiptOrder(BatchReceiptHeadDto batchReceiptHeadDto, String logSign) {
        List<OrderSignHead> orderSignHeads = new ArrayList<>();
        CommonResult<Object> res = new CommonResult<>();
        batchReceiptHeadDto.getHeadDtos().stream().forEach(receiptHeadDto -> {
            receiptHeadDto.setParty(batchReceiptHeadDto.getParty());
            OrderSignHead orderSignHead = this.genSignOrder(receiptHeadDto, logSign);
            if (orderSignHead != null) {
                orderSignHeads.add(orderSignHead);
                //if (PayStatus.PAID.getValue().equals(orderSignHead.getPayStatus())) {
                //已支付状态，回调商城退货退款
                OmsEventBus.post(orderSignHead);
                //}
            }
        });
        if (orderSignHeads.size() == batchReceiptHeadDto.getHeadDtos().size()) {
            res.setCode(ReturnCode.REQUEST_SUCCESS);
            res.setData(orderSignHeads);
        } else {
            res.setCode(ReturnCode.REQUEST_FAIL);
        }

        return res;
    }

    private OrderSignHead genSignOrder(ReceiptHeadDto receiptHeadDto, String logSign) {
        Long shippingOrderId = receiptHeadDto.getShippingOrderId();

        OrderShippingHeadWithBLOBs osh = this.shippingService.selectOneByShipCode(shippingOrderId, true);
        if (osh == null) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "此订单未生成发货单.请查询");
        }

        OrderHead orderHead = orderQueryService.findByCode(osh.getOrderId(), true, false);
        if (orderHead == null) {
            throw new BusinessException(ReturnCode.FIND_ORDER_IS_NULL, "用户订单号未查询到订单");
        }
        //签收发生在发货以后
        if (orderHead.getOrderStatus().compareTo(OrderStatus.ORDER_SORTING.getIndex()) < 0) {
            throw new BusinessException(ReturnCode.ORDER_STATUS_ERROR, "订单状态是" + OrderStatus.getName(orderHead.getOrderStatus()) + "，请检验。");
        }
        String lockKey = "oms:receipt:2:" + receiptHeadDto.getShippingOrderId().toString();
        if (!redisTemplate.lock(lockKey, "receipt", 3L)) {
            throw new BusinessException(ReturnCode.FIND_ORDER_IS_NULL, "操作太频繁,请稍后再试");
        }
        try {
            return this.createReceiptBill(receiptHeadDto, orderHead, osh, logSign);
        } catch (Exception e) {
            throw e;
        } finally {
            redisTemplate.unlock(lockKey, "receipt");
        }
    }


    /**
     * 更新签收单
     *
     * @param signHeadDto 请求参数
     * @param logSign     唯一标识
     * @return 返回信息
     */
    @Transactional
    public CommonResult<Object> updateReceiptOrder(SignHeadDto signHeadDto, String logSign) {

        Long receiptOrderId = signHeadDto.getReceiptOrderId();
        //将传入的下划线数据转成驼峰的
        Map<String, Object> updateFileds = StringTool.lineToHump(signHeadDto.getFileds());

        OrderSignHead orderSignHead = this.signOrderService.getSignHead(receiptOrderId, false);
        if (orderSignHead == null) {
            throw new BusinessException(ReturnCode.ORDER_SIGN_NULL_ERROR, "签收单不存在");
        }

        this.fullExtInfo(updateFileds, orderSignHead);
        logger.info(logSign + "commonResult-receipt- receiptOrderId {}, 转换后参数 {}", signHeadDto.getReceiptOrderId(), JSON.toJSONString(updateFileds));
        CommonResult<Object> res = new CommonResult<>();

        String lockKey = "oms:receipt:update" + signHeadDto.getReceiptOrderId().toString();
        if (!redisTemplate.lock(lockKey, "receipt", 3L)) {
            throw new BusinessException(ReturnCode.FIND_ORDER_IS_NULL, "操作太频繁,请稍后再试");
        }
        try {
            signOrderService.updatSingHeadInfo(receiptOrderId, updateFileds, logSign);
        } catch (Exception e) {
            throw e;
        } finally {
            redisTemplate.unlock(lockKey, "receipt");
        }

        res.setCode(ReturnCode.REQUEST_SUCCESS);
        return res;
    }

    /**
     * @param orderHead 订单信息
     * @param osh       发货单信息
     * @param logSign   信息标识
     * @return OrderSignHead
     */
    private OrderSignHead createReceiptBill(ReceiptHeadDto receiptHeadDto, OrderHead orderHead, OrderShippingHeadWithBLOBs osh, String logSign) {

        String md5Str = MD5Util.encrypt(receiptHeadDto.getSortdStr());
        // 生成参数md5数据，如果出现不一致，直接报错（不允许多次签收，如果签收数据是一样的，则直接返回）
        OrderSignHead oshHead = this.signOrderService.getSignHeadByOrderId(osh.getOrderId(), true);
        if (oshHead != null) {
            String md5StrDB = oshHead.getMd5Str();
            logger.info("param md5:[" + md5Str + "] db md5:[" + md5StrDB + "]");
            if (!md5Str.equals(md5StrDB)) {
                //签收单数据不一致，如果是未支付的单子，设置成无效，然后再生成签收单
                if (oshHead.getPayStatus().compareTo(PayStatus.PAID.getValue()) == 0) {
                    throw new BusinessException(ReturnCode.ORDER_SIGN_ERROR, "订单已签收(已支付)，不能再次签收");
                }
                //设置原签收单为无效数据
                oshHead.setIsValid(Valid.DISABLE.getValue());
            } else {
                return oshHead;
            }
        }

        List<OrderShippingDetail> shippingDetails = shippingService.getDetails(osh.getShippingOrderId());
        Map<String, ReceiptItem> codeItemMap = new HashMap<>();
        for (OrderShippingDetail shippingDetail : shippingDetails) {
            ReceiptItem receiptItem = new ReceiptItem(shippingDetail.getItemId(), shippingDetail.getSupplierOrg());

            String detailKey = shippingDetail.getCode() + ":" + shippingDetail.getSkuId();
            codeItemMap.put(detailKey, receiptItem);
        }

        CalOrderContent calOrderContent = this.orderReceipt(receiptHeadDto.toCalServiceSukList(), osh, shippingDetails, logSign);
        CalOrderReceiptContent calReceiptContent = calOrderContent.getReceipt();
        //deal
        OrderSignHead orderSignHead = this.receiptHeadBuider(orderHead, osh, calReceiptContent, codeItemMap, logSign, md5Str);
        //更新
        ShippingStatus shippingStatus = ShippingStatus.DELIVERED;
        OrderStatus orderStatus = OrderStatus.ORDER_SIGN;
        PayType payType = PayType.indexOf(orderHead.getPayType());
        if (PayType.PAY_ON_DELIVERY.equals(payType)) {
            shippingStatus = ShippingStatus.DELIVERING;
            orderStatus = OrderStatus.ORDER_DELIVERY;
        }

        if (!signOrderService.saveOrUpdateSignData(receiptHeadDto, orderSignHead, logSign, orderStatus, shippingStatus, calOrderContent.getReturnContent(), orderHead, oshHead)) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "添加签收单失败");
        } else {
            this.notifyEs(osh, orderHead, logSign, orderStatus, shippingStatus, oshHead);

            this.smsService.toSignSMS(orderSignHead, orderHead, orderHead.getOrderCode() + "");

            this.notifyDmall(orderHead);
        }

        signs.findById(orderSignHead.getReceiptOrderId()).filter($ -> $.paid()).ifPresent($ -> {
            signOrders.createReturnRequest(false, $, receiptHeadDto.getParty());
            signOrders.createIssueTask($);
        });

        return orderSignHead;
    }


    private void notifyDmall(OrderHead orderHead) {

        JSONObject orderext = JSON.parseObject(orderHead.getAddressInfo());
        String dmallOrderId = orderext.getString("DuoDianOrderId");

        if (StringUtils.isEmpty(dmallOrderId)) {
            return;
        }

        try {
            JSONObject req = new JSONObject();
            req.put("maxOrderId", orderHead.getOrderCode() + "");
            req.put("orderId", dmallOrderId);
            if (dmallProxy.pushDmall(req)) {
                redisTemplate.set(Constants.PUSH_FINISH_KEY + orderHead.getOrderCode(), "push");
            } else {
                logger.info("【推送多点订单已送达】已送达失败订单号 {}", orderHead.getOrderCode());
            }
        } catch (Exception e) {
            logger.error("【推送多点订单已送达】已送达失败订单号 {} 失败", orderHead.getOrderCode());
        }
    }


    /**
     * @param osh     发货单信息
     * @param logSign 信息标识
     * @return OrderSignHead
     */
    private CalOrderContent proxyCal(ReceiptHeadDto receiptHeadDto, OrderShippingHeadWithBLOBs osh, String logSign) {

        List<OrderShippingDetail> shippingDetails = shippingService.getDetails(osh.getShippingOrderId());
        //TODO:请求签收计算服务
        return this.orderReceipt(receiptHeadDto.toCalServiceSukList(), osh, shippingDetails, logSign);
    }

    protected OrderSignHead receiptHeadBuider(OrderHead orderHead, OrderShippingHeadWithBLOBs osh, CalOrderReceiptContent content, Map<String, ReceiptItem> codeItemMap, String logSign, String md5Str) {

        //获取支付信息
        List<BillPaymentInfo> billPaymentInfoList = billPaymentService.selectList(orderHead.getParentOrderCode().equals(0L) ? orderHead.getOrderCode() : orderHead.getParentOrderCode());

        OrderSignHead oshHead = OrderFactory.createSignHead(orderHead, osh, content.getHead(), billPaymentInfoList);
        oshHead.setfOrderId(orderHead.getParentOrderCode());
        long receiptOrderId = oshHead.getReceiptOrderId();
        List<OrderSignDetail> signDetails = this.receiptDetails(oshHead, content.getDetailList(), orderHead, receiptOrderId, codeItemMap, logSign);

        oshHead.setDetails(signDetails);
        oshHead.setMd5Str(md5Str);

        BigDecimal skuCount = BigDecimal.ZERO;
        BigDecimal realSkuCount = BigDecimal.ZERO;
        for (OrderSignDetail signDetail : signDetails) {
            skuCount = skuCount.add(signDetail.getQty());
            realSkuCount = realSkuCount.add(signDetail.getRealQty());
        }
        oshHead.setSkuCount(skuCount);
        oshHead.setRealSkuCount(realSkuCount);

        return oshHead;
    }


    /**
     * @param detailList     签收详情
     * @param orderHead      订单信息
     * @param receiptOrderId 签收单ID
     * @param logSign        唯一标识
     * @return List<OrderSignDetail>
     */
    private List<OrderSignDetail> receiptDetails(OrderSignHead oshHead, List<CalOrderReceiptDetail> detailList, OrderHead orderHead, Long receiptOrderId, Map<String, ReceiptItem> codeItemMap, String logSign) {
        List<OrderSignDetail> signDetails = new ArrayList<>();
        int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        try {

            BigDecimal totalQty = BigDecimal.ZERO;
            BigDecimal totalRealQty = BigDecimal.ZERO;

            for (CalOrderReceiptDetail detail : detailList) {
                if (detail == null) {
                    logger.info(logSign + "订单号" + orderHead.getOrderCode() + ",签收计算服务解析明细为空。");
                    throw new BusinessException(ReturnCode.REQUEST_FAIL, "此订单未生成发货单.请查询");
                }

                logger.info("签收计算服务详情 " + JSON.toJSONString(detail));
                logger.info("发货详情服务返回结果 " + JSON.toJSONString(codeItemMap));
                OrderSignDetail signDetail = new OrderSignDetail();
                signDetail.setReceiptOrderId(receiptOrderId);
                signDetail.setZoneId(orderHead.getRegionCode());

                JSONObject ext = detail.getExt();
                signDetail.setPackageId(ext.getInteger("package_id"));
                signDetail.setExt(ext.toJSONString());

                String detailKey = ext.getString("wm_code") + ":" + detail.getSkuId().toString();
                ReceiptItem receiptItem = codeItemMap.get(detailKey);
                if (receiptItem == null) {

                    throw new BusinessException(ReturnCode.REQUEST_FAIL, "签收数据与发货数据不一致");
                }

                signDetail.setItemId(receiptItem.getItem_id());
                signDetail.setSupplierOrg(receiptItem.getSupplier_org());

                totalQty = totalQty.add(detail.getQty());
                totalRealQty = totalRealQty.add(detail.getRealQty());
                signDetail.setRealQty(detail.getRealQty());
                signDetail.setSkuId(detail.getSkuId());
                signDetail.setSkuName(detail.getSkuName());
                signDetail.setSaleUnit(detail.getSaleUnit());
                signDetail.setQty(detail.getQty());
                signDetail.setPrice(detail.getPrice());
                signDetail.setMoney(detail.getMoney());
                signDetail.setStatus(Valid.ENABLE.getValue());
                signDetail.setCreatedAt(time);
                signDetail.setUpdatedAt(time);
                signDetail.setVenderId(orderHead.getVenderId());
                // TODO 2019-07-10
                Integer itemCode = detail.getItemCode();
                if (null == itemCode) {
                    itemCode = 0;
                }
                signDetail.setItemCode(itemCode);
                signDetail.setTmsId(0L);

                signDetails.add(signDetail);
            }
            oshHead.setSkuCount(totalQty);
            oshHead.setRealSkuCount(totalRealQty);
        } catch (Exception e) {
            logger.error(logSign + "订单号" + orderHead.getOrderCode() + ",签收计算服务解析明细失败。");
            logger.error(e.getMessage(), e);

            throw new BusinessException(ReturnCode.REQUEST_FAIL, "签收计算服务解析明细失败");
        }

        return signDetails;
    }

    private void fullExtInfo(Map<String, Object> fields, OrderSignHead orderSignHead) {


        if (fields.containsKey("ext")) {
            //如果存在ext，则需要diff数据
            String extStr = (String) fields.get("ext");
            Map<String, Object> extPro = orderSignHead.extendedProperties();
            Map<String, Object> updatePro = JSON.parseObject(extStr);

            for (String key : updatePro.keySet()) {
                extPro.put(key, updatePro.get(key));
            }

            fields.put("ext", JSON.toJSONString(extPro));
        }
    }

}
